#include <iostream>

#include "s_server.h"
#include "s_srp_database.h"
#include "s_connection.h"

#include "dhd_fs.pb.h"

namespace dhd
{
namespace server
{
   using namespace std;

   using boost::asio::mutable_buffer;

   using boost::asio::detail::socket_ops::network_to_host_short;


   Connection::Connection( tcp::endpoint * _endpoint, 
                           tcp::socket   * _sock, 
                           ssl_socket    * _ssl_sock,
                           Server        & _server ) : 
       endpoint(_endpoint), server(_server), fuse_backend( _server.get_serv_root() ),
       srp_ver(0)
   {
       sock_io = new SocketIO( _sock, _ssl_sock,
                               boost::bind( &Connection::handle_message,  this, _1, _2, _3 ),
                               boost::bind( &Connection::on_read_error,   this, _1, _2 ),
                               true );
       
       // Flag as authenticated if we have no database
       authenticated = !server.get_srp_db();
   }


   Connection::~Connection()
   {
       delete endpoint;
       
       if (srp_ver)
           srp_verifier_delete( srp_ver );
   }

   

   void Connection::on_read_error( const char * msg, bool reconnect )
   {
       server.connection_closed( this );
   }

   

   void Connection::handle_message( uint16_t request_id, PBuffer header, PBuffer buffer )
   {
       ToServer request;
       ToClient reply;
       PBuffer  pdata;
       bool     auth_failed = false;
       
       
       if ( !request.ParseFromArray( header->data(), header->size() ) )
           throw BadProtocol("Failed to parse reply message", false);
       
       std::cerr << "### REQUEST ID: " << request_id << std::endl;
       
       
       if ( authenticated )
       {
           pdata = fuse_backend.handle_request( request, reply, buffer );
       }
       else 
       {
           reply.set_msg_type( dhd::DHD_AUTHENTICATION );
           reply.set_result(0);
        
           if (request.has_auth())
           {
               if ( !srp_ver )
               {
                   // Step 1
                   if ( request.auth().has_username() && request.auth().has_a() )
                   {
                       std::string s;
                       std::string B;
                       
                       srp_ver = server.get_srp_db()->get_verifier( request.auth().username(), request.auth().a(), s, B );
                       
                       if (srp_ver)
                       {
                           ::dhd::Authentication* auth = reply.mutable_auth();
                           
                           auth->set_s(s);
                           auth->set_b(B);
                       }
                       else
                       {
                           std::cerr << "Failed to get validator for " << request.auth().username() << std::endl;
                           auth_failed = true;
                       }
                       
                   }
                   else
                   {
                       std::cerr << "Missing username and/or A for authentication packet" << std::endl;
                       auth_failed = true;
                   }
               }
               else
               {
                   // Step 2
                   if ( request.auth().has_m() )
                   {
                       const unsigned char * bytes_hamk;
                       
                       srp_verifier_verify_session( srp_ver, (const unsigned char *)request.auth().m().data(), &bytes_hamk );
                       
                       if ( bytes_hamk )
                       {
                           std::string hamk((const char *)bytes_hamk, SRP_SHA_DIGEST_LENGTH);
                           ::dhd::Authentication* a = reply.mutable_auth();
                           
                           a->set_hamk( hamk );
                           
                           authenticated = true;
                           srp_verifier_delete( srp_ver );
                           srp_ver = 0;
                           
                           std::cerr << "SRP AUTH SUCCESS!!!!" << std::endl;
                       }
                       else
                       {
                           std::cerr << "Auth failed on M verfication" << std::endl;
                           auth_failed = true;
                       }
                   }
                   else
                   {
                       std::cerr << "Missing M for 2nd step authentication packet" << std::endl;
                       auth_failed = true;
                   }
               }
           }        
       }
       
       
       if ( auth_failed )
       {
           throw BadProtocol("Authentication Failed", false);
       }
       else
       {
           PBuffer pbuff = allocate_buffer( reply.ByteSize() );
           
           if (!reply.SerializeToArray( pbuff->data(), pbuff->size() ))
               throw BadProtocol("Failed to serialize a message", false);

           std::cerr << "#####################################" << std::endl;
           std::cerr << reply.ShortDebugString() << std::endl;
           std::cerr << "#####################################" << std::endl;
           sock_io->enqueue( request_id, pbuff, pdata );
       }
   }
   
   

   void Connection::send_broadcast( PBuffer header, PBuffer buffer )
   {
       sock_io->enqueue( 0, header, buffer );
   }
      
}//end namespace dhd::server
}//end namespace dhd

